#include <bits/stdc++.h>
using namespace std;
//https://www.nowcoder.com/practice/80b6bb8797644c83bc50ac761b72981c?tpId=196&tqId=37172&ru=/exam/oj
// class Solution {
// public:
//     bool solve(string a, string b) {
//         string temp1,temp2;
//         if(a==b)return true;
//         if(b.find(a)!=std::string::npos)return false;
//         for(int i=0;i<a.size();i++)
//         {
//             temp1=a.substr(0,i+1),temp2=a.substr(i+1);
//             // cout<<temp1<<" "<<temp2<<endl;
//             string temp=temp2+temp1;
//             // cout<<temp<<endl;
//             if(b.find(temp)!=std::string::npos)
//             {
//                 return true;
//             }
//         }
//         return false;
        //解法二
        // if(a.size()!=b.size())return false;
        // return (a+a).find(b)!=string::npos;
//     }
// };
// int main()
// {
//     string a,b;
//     cin>>a>>b;
//     cout<<Solution().solve(a,b)<<endl;
//     return 0;
// }

//https://www.nowcoder.com/practice/65cfde9e5b9b4cf2b6bafa5f3ef33fa6?tpId=196&tqId=37081&ru=/exam/oj
struct ListNode {
    int val;
    struct ListNode *next;
	ListNode(int x) : val(x), next(nullptr) {}
};

// //拿不出来数据
// class Solution {
// public:
//     ListNode* mergeKLists(vector<ListNode*>& lists) {
//         vector<int> ret;
//         for(auto x:lists)
//         {
//             ListNode* cur=x;
//             while(cur)
//             {
//                 ret.push_back(cur->val);
//                 cur=cur->next;
//             }
//         }
//         if(ret.empty())return nullptr;
//         sort(ret.begin(),ret.end());
//         ListNode* head=new ListNode(ret[0]);
//         ListNode* cur=head;
//         for(int i=1;i<ret.size();i++)
//         {
//             cur->next=new ListNode(ret[i]);
//             cur=cur->next;
//         }
//         return head;
//     }
// };
// class Solution {
// public:
//     ListNode* mergeKLists(vector<ListNode*>& lists) {
//         priority_queue<int,vector<int>,greater<int>> p;
//         for(auto x:lists)
//         {
//             ListNode* cur=x;
//             while(cur)
//             {
//                 p.push(cur->val);               
//                 cur=cur->next; 
//             }
//         }
//         if(p.empty())return nullptr;
//         ListNode* cur=new ListNode(p.top());
//         p.pop();
//         if(p.empty())return cur;
//         ListNode* head=cur;
//         while(!p.empty())
//         {
//             cur->next=new ListNode(p.top());
//             p.pop();
//             cur=cur->next;
//         }
//         return head;
//     }
// };

//只取出每个vector中链表头节点的值，因为即使你取出第一个单链表的所有元素
//后面的单链表中的值可能比优先队列中现存储的值小，那么先存储的值就多占据了
//一些不必要的空间，优化做法可以减少前期的空间占用，较少了push的时间，
// class Solution {
// public:
//     struct cmp{bool operator()(ListNode* l1,ListNode*l2){return l1->val>l2->val;}};
//     ListNode* mergeKLists(vector<ListNode*>& lists) {
//         priority_queue<ListNode*,vector<ListNode*>,cmp> q;
//         for(auto x:lists)if(x)q.push(x);
//         ListNode* dummy=new ListNode(0);
//         ListNode* head=dummy;
//         while(!q.empty())
//         {
//             ListNode* t=q.top();
//             q.pop();
//             head->next=t;
//             head=head->next;
//             if(t->next)q.push(t->next);
//         }
//         return dummy->next;
//     }
// };
// int main()
// {
    
//     ListNode* node1=new ListNode(1);
//     ListNode* cur1=node1;
//     node1->next=new ListNode(2);
//     node1=node1->next;
//     node1->next=new ListNode(3);
//     node1=node1->next;

//     ListNode* node2=new ListNode(4);
//     ListNode* cur2=node2;

//     node2->next=new ListNode(5);
//     node2=node2->next;
//     node2->next=new ListNode(6);
//     node2=node2->next;
//     node2->next=new ListNode(7);
//     node2=node2->next;
//     vector<ListNode*> ret{cur1,cur2};
//     ListNode*node3=Solution().mergeKLists(ret);
//     while(node3)
//     {
//         cout<<node3->val<<" ";
//         node3=node3->next;
//     }
//     cout<<endl;
//     delete(node1);
//     delete(node2);
//     delete(node3);
//     return 0;
// }

//https://www.nowcoder.com/practice/36d613e0d7c84a9ba3af3ab0047a35e0?tpId=230&tqId=39760&ru=/exam/oj
int n,m,maxlen;
int dx[4]={0,0,1,-1},dy[4]={1,-1,0,0};
int dfs(vector<vector<int>>& ret,vector<vector<int>>& dp,int i,int j)
{
    if(dp[i][j])return dp[i][j];
    int len=1;
    for(int k=0;k<4;k++)
    {
        int x=i+dx[k],y=j+dy[k];
        if(x>=0&&x<n&&y>=0&&y<m&&ret[x][y]<ret[i][j])
        {
            len=max(len,1+dfs(ret,dp,x,y));
        }
    }
    dp[i][j]=len;
    return len;
}
int main()
{
    cin>>n>>m;
    // cout<<n*m<<endl;
    vector<vector<int>> ret(n,vector<int>(m,0));
    vector<vector<int>> dp(n,vector<int>(m,0));
    for(int i=0;i<n;i++)
        for(int j=0;j<m;j++)
        {
            cin>>ret[i][j];
        }
    for(int i=0;i<n;i++)
        for(int j=0;j<m;j++)
        {
            // vis[i][j]=true;
            maxlen=max(maxlen,dfs(ret,dp,i,j));
            // vis[i][j]=false;
        }
    cout<<maxlen<<endl;
    return 0;
}